package com.bestv.bsr.core.configuration;

import com.bestv.bsr.core.annotations.OptionalComponent;
import com.bestv.bsr.core.annotations.OptionalComponentScan;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.lang.NonNull;
import org.springframework.util.ClassUtils;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @author taojiacheng
 */
@Slf4j
public class OptionalComponentRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {

    private Environment environment;

    /**
     * @param annotationMetadata annotation metadata of the importing class
     * @param registry           current bean definition registry
     */
    @Override
    public void registerBeanDefinitions(@NonNull AnnotationMetadata annotationMetadata, @NonNull BeanDefinitionRegistry registry) {
        // 获取当前环境
        String property = environment.getProperty("spring.profiles.active");
        if (StringUtils.isEmpty(property)) {
            property = "standalone";
        }
        OptionalComponent.Strategy strategy = OptionalComponent.Strategy.STANDALONE;
        try {
            strategy = OptionalComponent.Strategy.valueOf(property.toUpperCase());
        } catch (Exception e) {
            log.warn("OptionalComponentRegistrar: strategy not found, use default strategy: {}", strategy);
        }
        Map<String, AnnotatedBeanDefinition> storedBeanDefinitionMap = new HashMap<>(25);
        ClassPathScanningCandidateComponentProvider scanner = getScanner();
        scanner.addIncludeFilter(new AnnotationTypeFilter(OptionalComponent.class));
        // 扫描路径
        Set<String> basePackages = getBasePackages(annotationMetadata);
        Set<BeanDefinition> candidateComponents = new HashSet<>();
        basePackages.forEach(basePackage -> candidateComponents.addAll(scanner.findCandidateComponents(basePackage)));
        for (BeanDefinition candidateComponent : candidateComponents) {
            if (candidateComponent instanceof AnnotatedBeanDefinition) {
                AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) candidateComponent;
                AnnotationMetadata beanDefinitionMeta = annotatedBeanDefinition.getMetadata();
                // optService attributes
                Map<String, Object> annotationAttributes = beanDefinitionMeta
                        .getAnnotationAttributes(OptionalComponent.class.getCanonicalName());
                if (annotationAttributes == null) {
                    continue;
                }
                if (!strategy.equals(annotationAttributes.get("strategy"))) {
                    continue;
                }
                String beanName = (String) annotationAttributes.get("value");
                AnnotatedBeanDefinition storedBeanDefinition = storedBeanDefinitionMap.get(beanName);
                if (storedBeanDefinition != null) {
                    // 准备注册的对象是否为 basic
                    Object basic = annotationAttributes.get("basic");
                    if (basic != null && (boolean) basic) {
                        continue;
                    }
                    throw new IllegalStateException("beanName: " + beanName + " already exists");
                }
                storedBeanDefinitionMap.put(beanName, annotatedBeanDefinition);
            }
        }
        // 注册bean
        storedBeanDefinitionMap.forEach(registry::registerBeanDefinition);
    }


    protected Set<String> getBasePackages(AnnotationMetadata importingClassMetadata) {
        Map<String, Object> attributes = importingClassMetadata
                .getAnnotationAttributes(OptionalComponentScan.class.getCanonicalName());

        Set<String> basePackages = new HashSet<>();
        assert attributes != null;
        for (String pkg : (String[]) attributes.get("basePackages")) {
            if (StringUtils.isNotEmpty(pkg)) {
                basePackages.add(pkg);
            }
        }
        if (basePackages.isEmpty()) {
            basePackages.add(ClassUtils.getPackageName(importingClassMetadata.getClassName()));
        }
        return basePackages;
    }


    protected ClassPathScanningCandidateComponentProvider getScanner() {
        return new ClassPathScanningCandidateComponentProvider(false) {
            @Override
            protected boolean isCandidateComponent(@NonNull AnnotatedBeanDefinition beanDefinition) {
                boolean isCandidate = false;
                if (beanDefinition.getMetadata().isIndependent()) {
                    if (!beanDefinition.getMetadata().isAnnotation()) {
                        isCandidate = true;
                    }
                }
                return isCandidate;
            }
        };
    }

    @Override
    public void setEnvironment(@NonNull Environment environment) {
        this.environment = environment;
    }
}
